`timescale 1ns / 1ps
module Toggle_port(
/*********NAND flash Toggle mode Port*********/
    output      WE_s,   
    output      RE_s,   
    output      CLE,   
    output      ALE,  

    input[7:0]  DQ_i,
    output[7:0] DQ_o, 
    output      DQ_oen, 

    input       DQS_i,
    output      DQS_o,
    output      DQS_oen, 

/*********************************************/
    input       clkX2,
    input       dclk,
    input       rst_n,

    input[2:0]  Cmd,  //
    input[7:0]  InputVal,       //
    output      IV_En,
    output[7:0] ReadVal,
    output      RV_En,
    input[31:0] RWDataNum,
    input[31:0] RWDataCounter
);

wire[7:0] I_DQ;               
wire      I_DQS;
wire      clk_m;
wire      dqs_m;
wire[7:0] O_DQ;
wire      we_s_m;
wire      re_s_m;

reg[7:0]  O_DQ_r0;
reg[7:0]  O_DQ_r1;
reg[7:0]  val_r;
reg      dv_r;
reg      dqs_en;
reg      clk_en;
reg      ale_r;
reg      cle_r;
reg      write_en;
reg      dataIn_rdy;
reg      we_s_en;
reg      re_s_en;
reg      CAorD;
reg      re_s_auto;
reg      re_s_r;




assign I_DQ = DQ_i;
assign DQ_o = O_DQ;
assign DQ_oen = write_en;

assign I_DQS = DQS_i;
assign DQS_o = dqs_m;
assign DQS_oen = write_en;
assign dqs_m = dqs_en ? dclk : 1'b0;

assign ReadVal = val_r ;
assign RV_En =  dv_r;

parameter State_command =   3'b000;
parameter State_adderss =   3'b001;
parameter State_WriteData = 3'b010;
parameter State_ReadData  = 3'b011;
parameter State_Idle =      3'b100;
parameter State_WriteWait = 3'b111;

parameter Idle =      4'b1111;
parameter WriteWait = 4'b0000;
parameter Command =   4'b0001;
parameter WriteData = 4'b0010;
parameter ReadData =  4'b0100;
parameter Address =   4'b1000;

parameter dqsStartDelay = 8'h02;
parameter resStartDelay = 4'h1;


assign O_DQ = CAorD ? O_DQ_r0 : O_DQ_r1;
assign IV_En =  (Cmd == State_WriteData) ? dataIn_rdy : ~dclk ;
assign clk_m = clkX2;
assign WE_s =  we_s_m ;
assign we_s_m = ~(we_s_en & dclk);
assign RE_s =  re_s_m ;
assign re_s_m = re_s_auto ? ~(re_s_en & dclk) : re_s_r;
assign ALE = ale_r;
assign CLE = cle_r;

wire b0;
assign b0 = Cmd == State_command || Cmd == State_adderss;

always @(posedge clk_m or negedge rst_n) begin
    if(!rst_n) begin
        write_en <=  0;
        CAorD <=    1;
    end else begin
        if (Cmd == State_ReadData) begin
            write_en <= 0;
        end else begin
            write_en <= 1;
        end
        if (b0) begin
            CAorD <= 1;
        end else begin
            CAorD <= 0;
        end
    end
end

always @(posedge dclk or negedge rst_n) begin
    if (!rst_n) begin
        ale_r <= 1'b0;
        cle_r <= 1'b0;
        we_s_en <= 1'b0;
    end else begin
        case (Cmd)
           State_adderss : begin
                ale_r <=   1'b1;
                cle_r <=   1'b0;
                we_s_en <= 1'b1;
           end
           State_command : begin
                ale_r <=   1'b0;
                cle_r <=   1'b1;
                we_s_en <= 1'b1;
           end
           State_ReadData : begin
                ale_r <=   1'b0;
                cle_r <=   1'b0; 
                we_s_en <= 1'b0;
           end
           State_WriteData : begin
                ale_r <=   1'b0;
                cle_r <=   1'b0;
                we_s_en <= 1'b0;
           end
           State_Idle : begin
                ale_r <=   1'b0;
                cle_r <=   1'b0;
                we_s_en <= 1'b0;
           end
            default : begin
                ale_r <= ale_r;
                cle_r <= cle_r;
                we_s_en <= we_s_en;
            end 
        endcase
    end
end

always @(negedge clk_m or negedge rst_n) begin
    if (!rst_n) begin
        O_DQ_r0 <= 0;
    end else begin
        if (dclk & (Cmd == State_adderss || Cmd == State_command)) begin
            O_DQ_r0 <= InputVal;
        end else begin
            O_DQ_r0 <= O_DQ_r0;
        end
    end
end

reg[3:0] dqsCounter;
always @(posedge clk_m or negedge rst_n) begin
    if (!rst_n) begin
        dqsCounter <= 4'h0;
        dataIn_rdy <= 1'b0;
        dqs_en     <= 1'b0;    
    end else begin
        if (Cmd == State_WriteData) begin
            if (dqsCounter == dqsStartDelay & RWDataCounter != RWDataNum) begin
                dqsCounter <= dqsCounter;
                dataIn_rdy <= 1'b1;
                dqs_en     <= 1'b1;    
            end else begin
                dqsCounter <= dqsCounter + 1'b1;
                dataIn_rdy <= 1'b0;
                dqs_en     <= 1'b0;    
            end
        end else begin
            dqsCounter <= 4'h0;
            dataIn_rdy <= 1'b0;
            dqs_en     <= 1'b0;    
        end
    end
end

always @(negedge clk_m or negedge rst_n) begin
    if (!rst_n) begin
        O_DQ_r1 <= 0;
    end else begin
        if (IV_En && Cmd == State_WriteData) begin
            O_DQ_r1 <= InputVal;
        end else begin
            O_DQ_r1 <= O_DQ_r1;
        end
    end
end

reg[3:0] resCounter;
always @(posedge dclk or negedge rst_n) begin
    if (!rst_n) begin
        re_s_en <= 1'b0;
        resCounter <= 4'h0;
    end else begin
        if (Cmd == State_ReadData) begin
            if (resCounter == resStartDelay) begin
                re_s_en <= 1'b1;
            end else begin
                re_s_en <= 1'b0;
                resCounter <= resCounter + 1'b1;
            end
        end else begin
            re_s_en <= 1'b0;
            resCounter <= 4'h0;
        end
    end
end

always @(posedge dclk or negedge rst_n) begin
    if (!rst_n) begin
        re_s_auto <= 1'b1;
        re_s_r <= 1'b0;
    end else begin
        if (Cmd == State_ReadData) begin
            if (RWDataCounter == RWDataNum - 8'h02) begin
                re_s_r <= 1'b0;
                re_s_auto <= 1'b0;
            end if (RWDataCounter == RWDataNum) begin
                re_s_r <= 1'b1;
                re_s_auto <= 1'b1;
            end else begin
                re_s_r <= re_s_r;
                re_s_auto <= re_s_auto;
            end
        end else begin
            re_s_auto <= 1'b1;
            re_s_r <= 1'b0;
        end
    end
end

reg readStart;
always @(posedge I_DQS or negedge rst_n) begin
    if (!rst_n) begin// 
        readStart <= 1'b0;
    end else begin
        if (Cmd == State_ReadData & I_DQS == 1'b1) begin
            readStart <= 1'b1;
        end else begin
            readStart <= 1'b0;           
        end
    end
end

always @(negedge clk_m or negedge rst_n) begin
    if (!rst_n) begin
        val_r <= 0;
        dv_r <= 1'b0;
    end else begin
        if (Cmd == State_ReadData && readStart) begin
            val_r <= I_DQ;
            dv_r <= 1'b1;
        end else begin
            val_r <= val_r;
            dv_r <= 1'b0;
        end
    end
end

endmodule // Toggle_port